<!--

  ----------------介绍----------------
  管理界面组件是一个基于 Vue 框架开发的通用管理界面，
  用于展示数据、搜索、操作和分页等功能。该组件提供了丰富的选项和定制功能，
  使得用户可以方便地构建符合自己需求的管理界面。

  ----------------属性----------------
  searchColumn: Array，搜索表单的配置信息，详细见下方说明。
  tableColumn: Array，表格列的配置信息，详细见下方说明。
  getTable: Function，获取表格数据的方法。
  showLeftOption: Array，左侧操作按钮的显示配置，默认为 ['add', 'update', 'delete', 'export']。
  downloadLoading: Boolean，导出按钮的加载状态。
  permission: Object，操作权限的配置信息。
  disabledLeftOption: Object，左侧操作按钮是否禁用的配置信息。

  searchColumn 配置项说明
    type: String，字段类型，可选值为 'input' 或 'select'。
    dataIndex: String，字段对应的数据字段名。
    label: String，字段显示的标签。
    width: String，字段宽度。
    option: Array，当 type 为 'select' 时有效，表示选项的配置信息。

  tableColumn 配置项说明
    dataIndex: String，字段对应的数据字段名。
    title: String，列显示的标题。
    width: String，列宽度。
    type: String，列类型，可选值包括 'default'、'selection'、'index'。
    fixed: String，列固定位置，可选值为 'left' 或 'right'。
    align: String，列对齐方式，可选值为 'left'、'center'、'right'。
    render: Function，自定义渲染列内容的函数。
    renderHtml: Function，自定义渲染列内容为HTML标签的函数。
    slot: String，插槽名称，用于自定义插槽渲染列内容。
    option: Array，枚举选项的配置信息。

  ----------------事件----------------
  selectionChange(selection): 批量勾选事件，参数为勾选的数据。
  leftOptionChange(option): 左侧操作按钮点击事件，参数为点击的按钮标识。

  ----------------插槽----------------
  screen: 自定义搜索框的插槽。
  left_option: 自定义左侧操作按钮的插槽。

  ----------------方法----------------
  getTableData(data): 获取表格数据的方法，可传入参数进行筛选。

 -->
<script setup>
import { onMounted, reactive, ref, set, watch } from 'vue'
import { useGetEnum, propsWidth } from './public'
import { Message } from 'element-ui'

const { dictEnumReveal, getEnumlabel, optionFunc } = useGetEnum()

const props = defineProps([
  'searchColumn',
  'tableColumn',
  'getTable',
  'showLeftOption',
  'downloadLoading',
  'permission',
  'disabledLeftOption'
])
const emits = defineEmits(['selectionChange', 'leftOptionChange'])

const newTableColumn = ref([]) // 处理后的表格列 带有 checked 属性
const tableData = ref([]) // 表格数据
// 搜索用的表单数据
const formData = reactive({
  select: {}, // 选择框
  input: {} // 输入框
})
// 获取表格数据
const elTableRef = ref(null)
const getTableData = (data = {}) => {
  const { page, size } = pageInfo
  const params = {
    ...data,
    page: page - 1,
    size,
    sort: 'update_time,desc'
  }
  return props.getTable(params).then((res) => {
    tableData.value = res.content
    total.value = res.totalElements
    elTableRef.value.doLayout()
  })
}
// 点击搜索
const onSearch = (isSearch) => {
  if (isSearch) pageInfo.page = 1
  const params = {
    ...formData.input,
    ...formData.select
  }
  getTableData(params)
}
// 点击重置
const onReset = () => {
  pageInfo.page = 1
  Object.keys(formData).forEach((key) => {
    formData[key] = {}
  })
  getTableData()
}

const showScreen = ref(true) // 是否显示 搜索板块
// 右侧三个按钮的事件监听
const optionBtnClick = (key) => {
  switch (key) {
    case 'isShow':
      showScreen.value = !showScreen.value
      break
    case 'refresh':
      onReset()
      break
    case 'more':
      break
    default:
      emits('leftOptionChange', key)
      break
  }
}

// 表格批量勾选的监听
const handleSelectionChange = (e) => {
  emits('selectionChange', e)
}

const total = ref(0) // 列表总条数
const pageInfo = reactive({
  page: 1,
  size: 20
})
// 监听分页器的变化
const handleSizeChange = (e) => {
  pageInfo.size = e
  onSearch()
}
// 监听页码的变化
const handleCurrentChange = (e) => {
  pageInfo.page = e
  onSearch()
}

onMounted(() => {
  // 获取列表数据
  getTableData()
  // 初始化工具栏中的多选，用于表格列的显示和隐藏
  newTableColumn.value = props.tableColumn.map((item) => {
    set(item, 'checked', true)
    return item
  })
})
const allChecked = ref(false) // 全选
const isIndeterminate = ref(false)

const handleCheckAllChange = (val) => {
  const checkedCount = newTableColumn.value.filter((item) => item.checked).length
  if (checkedCount === newTableColumn.value.length) {
    allChecked.value = true
    Message.warning('至少保留一个选项')
    return
  }
  newTableColumn.value.forEach((item) => {
    if (item.title && item.title !== '操作') {
      item.checked = val
    }
  })
  isIndeterminate.value = false
}

const handleCheckChange = (e) => {
  const checkedCount = newTableColumn.value.filter((item) => item.checked).length
  allChecked.value = checkedCount === newTableColumn.value.length
  isIndeterminate.value =
    checkedCount > 0 && checkedCount < newTableColumn.value.length

  // 当取消勾选时，如果只剩一个选项被选中，则阻止取消，并提示
  if (checkedCount === 2 || checkedCount === 0) {
    e.checked = true
    Message.warning('至少保留一个选项')
  }
}

// 监听newTableColumn的变化，更新全选和中间状态
watch(newTableColumn, handleCheckChange)

defineExpose({
  getTableData,
  onSearch,
  tableData
})
</script>

<template>
  <div class="pro-table-container">
    <section v-show="showScreen" class="screen-box box-shadow">
      <template v-for="item in props.searchColumn">
        <el-input
          v-if="item.type === 'input'"
          v-model="formData.input[item.dataIndex]"
          :placeholder="item.label"
          :style="{ width: propsWidth(item.width) || '195px' }"
        />
        <el-select
          v-if="item.type === 'select'"
          v-model="formData.select[item.dataIndex]"
          :placeholder="item.label"
          :clearable="item.clearable"
          :style="{ width: propsWidth(item.width) || '195px' }"
        >
          <el-option
            v-for="enums in optionFunc(item.option)"
            :key="item.optionValue ? enums[item.optionValue] : enums.value"
            :label="item.optionLabel ? enums[item.optionLabel] : enums.label"
            :value="item.optionValue ? enums[item.optionValue] : enums.value"
          />
        </el-select>
      </template>
      <slot name="screen" />
      <el-button type="primary" size="mini" @click="onSearch(true)">搜索</el-button>
      <el-button size="mini" style="margin: 0" @click="onReset">重置</el-button>
    </section>
    <section class="option-box">
      <div class="left-box box-shadow">
        <el-button
          v-show="props.showLeftOption.includes('add')"
          v-permission="props.permission.add"
          type="primary"
          size="mini"
          icon="el-icon-plus"
          @click="optionBtnClick('add')"
        >
          新增
        </el-button>
        <el-button
          v-show="props.showLeftOption.includes('update')"
          v-permission="props.permission.edit"
          type="warning"
          icon="el-icon-refresh"
          size="mini"
          @click="optionBtnClick('update')"
        >修改</el-button>
        <el-button
          v-show="props.showLeftOption.includes('export')"
          v-permission="props.permission.download"
          :loading="props.downloadLoading"
          :disabled="props.disabledLeftOption.export"
          type="primary"
          size="mini"
          icon="el-icon-upload2"
          @click="optionBtnClick('export')"
        >{{ props.disabledLeftOption.exportText || '导出' }}</el-button>
        <el-button
          v-show="props.showLeftOption.includes('delete')"
          v-permission="props.permission.del"
          type="danger"
          size="mini"
          icon="el-icon-delete"
          @click="optionBtnClick('delete')"
        >删除</el-button>
        <slot name="left_option" />
      </div>
      <div class="right-box box-shadow">
        <el-button-group>
          <el-button size="mini" icon="el-icon-search" @click="optionBtnClick('isShow')" />
          <el-button size="mini" icon="el-icon-refresh" @click="optionBtnClick('refresh')" />
          <el-popover placement="bottom" trigger="click">
            <div class="checkbox-box">
              <el-checkbox
                v-model="allChecked"
                :indeterminate="isIndeterminate"
                @change="handleCheckAllChange"
              >全选</el-checkbox>
              <template v-for="item in newTableColumn">
                <el-checkbox
                  v-if="item.title && item.title !== '操作'"
                  v-model="item.checked"
                  @change="handleCheckChange(item)"
                >
                  {{ item.title }}
                </el-checkbox>
              </template>
            </div>
            <el-button slot="reference" size="mini" icon="el-icon-s-grid" @click="optionBtnClick('more')" />
          </el-popover>
        </el-button-group>
      </div>
    </section>
    <section class="table-box box-shadow">
      <el-table ref="elTableRef" stripe height="calc(100vh - 350px)" :data="tableData" style="width: 100%;overflow-y: auto;" @selection-change="handleSelectionChange">
        <template v-for="item in newTableColumn">
          <template v-if="item.checked">
            <!-- 渲染 render -->
            <el-table-column
              v-if="item.render"
              :prop="item.dataIndex"
              :label="item.title"
              :width="propsWidth(item.width) || 'auto'"
            >
              <template slot-scope="scope">{{
                item.render(scope.row)
              }}</template>
            </el-table-column>
            <el-table-column
              v-if="item.renderHtml"
              :prop="item.dataIndex"
              :label="item.title"
              :width="propsWidth(item.width) || 'auto'"
            >
              <template slot-scope="scope">
                <div v-html="item.renderHtml(scope.row)" />
              </template>
            </el-table-column>
            <!-- 插槽 slot -->
            <el-table-column
              v-if="item.slot"
              :align="item.align ? item.align : 'left'"
              :prop="item.dataIndex"
              :label="item.title"
              :width="propsWidth(item.width) || 'auto'"
              :fixed="item.fixed ? item.fixed : null"
            >
              <template slot-scope="scope">
                <slot :name="item.slot" :row="scope.row" />
              </template>
            </el-table-column>
            <!-- 枚举 option -->
            <el-table-column
              v-if="item.option"
              :prop="item.dataIndex"
              :label="item.title"
              :width="propsWidth(item.width) || 'auto'"
            >
              <template slot-scope="scope">
                {{ getEnumlabel(item.option, scope.row[item.dataIndex]) }}
              </template>
            </el-table-column>
            <!-- 默认 -->
            <el-table-column
              v-if="!item.render && !item.slot && !item.option"
              :type="item?.type"
              :prop="item.dataIndex"
              :label="item.title"
              :width="propsWidth(item.width) || 'auto'"
              :fixed="item.fixed ? item.fixed : null"
            />
          </template>
        </template>
      </el-table>
    </section>
    <section class="box-shadow">
      <el-pagination
        layout="total, sizes, prev, pager, next, jumper"
        :page-sizes="[10, 20, 30, 40, 50, 100]"
        :current-page="pageInfo.page"
        :page-size="pageInfo.size"
        :total="total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </section>
  </div>
</template>

<style scoped lang="scss">
.pro-table-container {
  display: flex;
  background-color: #fff;
  flex-direction: column;
  height: 100%;
  gap: 19px;
  padding: 20px;

  .screen-box {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;

    :deep(.el-input) {
      width: 195px;
    }
  }

  .option-box {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .table-box {
    flex: 1;
    margin-bottom: -11px;
  }
}

.checkbox-box {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
</style>
